﻿using System;

namespace LogikBug.Async.Tasks
{
	/// <summary>
	/// Provides timestamps for when the <see cref="Agent"/> was
	/// initialized, started execution, completed execution,
	/// disposed, and the resulting value of the <see cref="Agent"/>'s execution.
	/// </summary>
	public class AgentResult
	{
		#region Constructors

		/// <summary>
		/// Initializes a new instance of the <see cref="AgentResult"/> class.
		/// </summary>
		public AgentResult()
		{
			IsSuccessful = false;
			InitializeTime = DateTime.Now;
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="AgentResult"/> class with
		/// the initial value as a result of the execution of the <see cref="Agent"/>.
		/// </summary>
		/// <param name="value">The value used to initialize the current instance.</param>
		public AgentResult(object value) : this()
		{
			Value = value;
		}

		#endregion

		#region Properties

		/// <summary>
		/// Gets or sets a value of whether the
		/// execution of the <see cref="Agent"/>
		/// was successful or not.
		/// </summary>
		public bool IsSuccessful { get; set; }

		/// <summary>
		/// Gets or sets the <see cref="DateTime"/>
		/// when the <see cref="Agent"/> was initialized.
		/// </summary>
		public DateTime InitializeTime { get; set; }

		/// <summary>
		/// Gets or sets the <see cref="DateTime"/>
		/// when the <see cref="Agent"/> started execution.
		/// </summary>
		public DateTime StartTime { get; set; }

		/// <summary>
		/// Gets or sets the <see cref="DateTime"/>
		/// when the <see cref="Agent"/> started disposing.
		/// </summary>
		public DateTime DisposingTime { get; set; }

		/// <summary>
		/// Gets or sets the <see cref="DateTime"/>
		/// when the <see cref="Agent"/> completed execution.
		/// </summary>
		public DateTime CompletionTime { get; set; }

		/// <summary>
		/// The result of the <see cref="Agent"/>s execution, if any.
		/// </summary>
		public object Value { get; set; }

		/// <summary>
		/// Gets the <see cref="Type"/> of result of the <see cref="Agent"/>.
		/// </summary>
		public Type Type
		{
			get
			{
				return GetValueType();
			}
		}

		#endregion

		#region Methods

		/// <summary>
		/// Gets the <see cref="Type"/> of result of the <see cref="Agent"/>.
		/// </summary>
		/// <returns></returns>
		protected Type GetValueType()
		{
			if (Value != null)
			{
				return Value.GetType();
			}

			return null;
		}

		/// <summary>
		/// Determines whether the <see cref="Agent"/> provided a result or not.
		/// </summary>
		/// <returns>True if the value is not null, False otherwise.</returns>
		public bool HasResult()
		{
			return Value != null;
		}

		/// <summary>
		/// Determines whether the value is of type <typeparamref name="TType"/> or not.
		/// </summary>
		/// <typeparam name="TType">The type to be determined.</typeparam>
		/// <returns>True if the result of the <see cref="Agent"/> is
		/// of type <typeparamref name="TType"/>, False otherwise.</returns>
		public bool ResultIs<TType>()
		{
			return HasResult() && Value is TType;
		}

		/// <summary>
		/// Returns the result of the <see cref="Agent"/>
		/// if it derives from <typeparamref name="T"/>,
		/// otherwise the default value of <typeparamref name="T"/>.
		/// </summary>
		/// <typeparam name="T">The type to return.</typeparam>
		/// <returns>
		/// Returns the result of the <see cref="Agent"/>
		/// if it derives from <typeparamref name="T"/>,
		/// otherwise the default value of <typeparamref name="T"/>.
		/// </returns>
		public T GetResultAs<T>()
		{
			if (ResultIs<T>())
			{
				return (T)Value;
			}

			return default(T);
		}

		#endregion
	}
}
